using System;
using System.Diagnostics.Contracts;
using Charisma.Metadata;

namespace Charisma
{
    public class MemberAccessor
    {
        public MemberAccessor(MetaField field)
        {
            Contract.Requires(field != null);

            Member = field;
        }

        public MetaField Member { get; private set; }

        public void SetValue(object instance, object key)
        {
            Member.SetValue(instance, key);
        }

        public bool HasValue(object instance)
        {
            object value = Member.GetValue(instance);
            if (value == null)
                return false;
            Type type = value.GetType();
            TypeCode code = Type.GetTypeCode(type);
            switch (code)
            {
                case TypeCode.Boolean:
                    return (bool) value;
                case TypeCode.Byte:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.SByte:
                case TypeCode.UInt16:
                    return (int) value != 0;
                case TypeCode.Decimal:
                case TypeCode.UInt64:
                    return (decimal) value != 0M;
                case TypeCode.Double:
                    return (double) value != 0.0D;
                case TypeCode.Single:
                    return (float) value != 0.0F;
                case TypeCode.UInt32:
                case TypeCode.Int64:
                    return (long) value != 0L;
                case TypeCode.String:
                    return !string.IsNullOrEmpty((string) value);
                case TypeCode.Char:
                    return (char) value != '\0';
                case TypeCode.DateTime:
                    return (DateTime) value != DateTime.MinValue;
                case TypeCode.Object:
                    if (type == typeof (Guid))
                        return (Guid) value != Guid.Empty;
                    if (type == typeof (TimeSpan))
                        return (TimeSpan) value != TimeSpan.Zero;
                    break;
                default:
                    return true;
            }
            return true;
        }

        public object GetValue(object instance)
        {
            return Member.GetValue(instance);
        }
    }
}